#include "web.h"
#include "app.h"

#include <map>
#include <algorithm>

namespace {
  struct sApiItem {
    std::string api_full;
    std::string api;
    utility::eApiType type;
    int method;
    std::string data;
  };
  struct sApis {
    std::map<std::string, sApiItem> simple;
    std::map<std::string, sApiItem> args;
    std::map<std::string, sApiItem> path;
  };
  sApis apis_;
  rwlock::Lock lock_;

  void parse_api_type(const std::string &full_api, utility::eApiType &type, std::string &api) {
    if (full_api.empty()) return;
    auto pos = full_api.find("<");
    if (pos == std::string::npos) {
      type = utility::eApiType::Simple;
      api = full_api;
    } else {
      api = full_api.substr(0, pos);
      auto epos = full_api.find(">", pos);
      if (epos == std::string::npos) type = utility::eApiType::Simple;
      else type = utility::get_api_type(full_api.substr(pos+1,epos-pos-1));
    }
  }
  sApiItem parse_a_api(const crow::json::rvalue& api) {
    sApiItem item;
    item.api_full = api["api"].s();
    parse_api_type(item.api_full, item.type, item.api);
    item.data = api["data"].s();
    auto mtd = api["method"];
    auto len = mtd.size();
    item.method = (int)utility::eHttpMethod::Get;
    for (size_t i = 0; i < len; ++i) {
      item.method |= (int)utility::get_method_type(mtd[i].s());
    }
    return std::move(item);
  }
  bool add_api(sApis& apis, const sApiItem& api) {
    if (api.type == utility::eApiType::Simple) {
      if (apis.simple.find(api.api) != apis.simple.end())
        return false;
      apis.simple[api.api] = api;
      return true;
    } else {
      if (apis.args.find(api.api) != apis.args.end())
        return false;
      if (apis.path.find(api.api) != apis.path.end())
        return false;
      if (api.type == utility::eApiType::Path) {
        apis.path[api.api] = api;
      } else {
        apis.args[api.api] = api;
      }
      return true;
    }
    return false;
  }
  bool init() {
    sApis apis;
    auto jApis = app::cfg.api_apis();
    auto len = jApis.size();
    for (size_t i = 0; i < len; ++i) {
      auto api = parse_a_api(jApis[i]);
      if (!add_api(apis, api)) {
        std::cout << "[API] Api exist: " << api.api << std::endl;
        continue;
      }
    }

    {
      rwlock::LockWrite _(lock_);
      apis_.simple.swap(apis.simple);
      apis_.args.swap(apis.args);
      apis_.path.swap(apis.path);
    }
    return true;
  }

  std::string handle(const crow::request& req, const std::string& url) {
    crow::json::wvalue x;
    x["method"] = (int)req.method;
    x["raw_url"] = req.raw_url;
    x["url"] = req.url;
    //x["url_params"] = req.url_params;
    //x["headers"] = headers;
    x["body"] = req.body;
    return crow::json::dump(x);
  }
}

namespace web {
  bool add_web(crow::SimpleApp &app) {
    if (!init()) return false;
    CROW_ROUTE(app, "/")([](const crow::request& req) {
      return handle(req, "/");
    });

    CROW_ROUTE(app, "/<path>")([](const crow::request& req, const std::string& file) {
      return handle(req, file);
    });
    return true;
  }
}